/*
 * Copyright (c) 2023 HPMicro
 *
 * SPDX-License-Identifier: BSD-3-Clause
 *
 */

#include "usbd_core.h"
#include "usbd_msc.h"
#include "flash_util.h"

#define APP_SIZE (256 * SIZE_1KB)
#define MSC_FLASH_START (APP_SIZE)

#define MSC_IN_EP  0x81
#define MSC_OUT_EP 0x02

#define USB_CONFIG_SIZE (9 + MSC_DESCRIPTOR_LEN)

#ifdef CONFIG_USB_HS
#define MSC_MAX_MPS 512
#else
#define MSC_MAX_MPS 64
#endif

const uint8_t msc_ram_descriptor[] = {
        USB_DEVICE_DESCRIPTOR_INIT(USB_2_0, 0x00, 0x00, 0x00, USBD_VID, USBD_PID, 0x0200, 0x01),
        USB_CONFIG_DESCRIPTOR_INIT(USB_CONFIG_SIZE, 0x01, 0x01, USB_CONFIG_BUS_POWERED, USBD_MAX_POWER),
        MSC_DESCRIPTOR_INIT(0x00, MSC_OUT_EP, MSC_IN_EP, MSC_MAX_MPS, 0x02),
        /*
         * string0 descriptor
         */
        USB_LANGID_INIT(USBD_LANGID_STRING),
        /*
         * string1 descriptor
         */
        0x14,                       /* bLength */
        USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */
        'C', 0x00,                  /* wcChar0 */
        'h', 0x00,                  /* wcChar1 */
        'e', 0x00,                  /* wcChar2 */
        'r', 0x00,                  /* wcChar3 */
        'r', 0x00,                  /* wcChar4 */
        'y', 0x00,                  /* wcChar5 */
        'U', 0x00,                  /* wcChar6 */
        'S', 0x00,                  /* wcChar7 */
        'B', 0x00,                  /* wcChar8 */
        /*
         * string2 descriptor
         */
        0x26,                       /* bLength */
        USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */
        'C', 0x00,                  /* wcChar0 */
        'h', 0x00,                  /* wcChar1 */
        'e', 0x00,                  /* wcChar2 */
        'r', 0x00,                  /* wcChar3 */
        'r', 0x00,                  /* wcChar4 */
        'y', 0x00,                  /* wcChar5 */
        'U', 0x00,                  /* wcChar6 */
        'S', 0x00,                  /* wcChar7 */
        'B', 0x00,                  /* wcChar8 */
        ' ', 0x00,                  /* wcChar9 */
        'M', 0x00,                  /* wcChar10 */
        'S', 0x00,                  /* wcChar11 */
        'C', 0x00,                  /* wcChar12 */
        ' ', 0x00,                  /* wcChar13 */
        'D', 0x00,                  /* wcChar14 */
        'E', 0x00,                  /* wcChar15 */
        'M', 0x00,                  /* wcChar16 */
        'O', 0x00,                  /* wcChar17 */
        /*
         * string3 descriptor
         */
        0x16,                       /* bLength */
        USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */
        '2', 0x00,                  /* wcChar0 */
        '0', 0x00,                  /* wcChar1 */
        '2', 0x00,                  /* wcChar2 */
        '3', 0x00,                  /* wcChar3 */
        '1', 0x00,                  /* wcChar4 */
        '2', 0x00,                  /* wcChar5 */
        '3', 0x00,                  /* wcChar6 */
        '4', 0x00,                  /* wcChar7 */
        '5', 0x00,                  /* wcChar8 */
        '6', 0x00,                  /* wcChar9 */
#ifdef CONFIG_USB_HS
        /*
         * device qualifier descriptor
         */
        0x0a,
        USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER,
        0x00,
        0x02,
        0x00,
        0x00,
        0x00,
        0x40,
        0x01,
        0x00,
#endif
        0x00
};

void usbd_event_handler(uint8_t event)
{
    switch (event) {
        case USBD_EVENT_RESET:
            break;
        case USBD_EVENT_CONNECTED:
            break;
        case USBD_EVENT_DISCONNECTED:
            break;
        case USBD_EVENT_RESUME:
            break;
        case USBD_EVENT_SUSPEND:
            break;
        case USBD_EVENT_CONFIGURED:
            break;
        case USBD_EVENT_SET_REMOTE_WAKEUP:
            break;
        case USBD_EVENT_CLR_REMOTE_WAKEUP:
            break;

        default:
            break;
    }
}

void usbd_msc_get_cap(uint8_t lun, uint32_t *block_num, uint16_t *block_size)
{
    hpm_stat_t status;
    uint32_t msc_flash_size;
    uint32_t num_of_blocks;
    nor_property_t nor_property;
    status = flash_get_property(&nor_property);
    if (status == status_success) {
        msc_flash_size = nor_property.flash_size - APP_SIZE;
        num_of_blocks = msc_flash_size / nor_property.sector_size;

        *block_num = num_of_blocks;    /* Pretend having so many buffer,not has actually. */
        *block_size = nor_property.sector_size;
    } else {
        *block_num = 0;
        *block_size = 0;
    }
}

int usbd_msc_sector_read(uint32_t sector, uint8_t *buffer, uint32_t length)
{
    hpm_stat_t status;
    nor_property_t nor_property;
    status = flash_get_property(&nor_property);
    if (status == status_success)
    {
        uint32_t offset = APP_SIZE + sector * nor_property.sector_size;
        status = flash_read(offset, buffer, length);
    }
    return status;
}

int usbd_msc_sector_write(uint32_t sector, uint8_t *buffer, uint32_t length)
{
    hpm_stat_t status;
    nor_property_t nor_property;
    status = flash_get_property(&nor_property);
    if (status == status_success)
    {
        uint32_t offset = APP_SIZE + sector * nor_property.sector_size;
        status = flash_write(offset, buffer, length);
    }
    return status;
}

/* function ------------------------------------------------------------------*/
/**
 * @brief            msc ram init
 * @pre              none
 * @param[in]        none
 * @retval           none
 */
struct usbd_interface intf0;

void msc_flash_init(void)
{
    flash_init();
    usbd_desc_register(msc_ram_descriptor);
    usbd_add_interface(usbd_msc_init_intf(&intf0, MSC_OUT_EP, MSC_IN_EP));

    usbd_initialize();
}
